﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace UDDL.Parser
{
	public class Tokenizer
	{
		public Tokenizer(String filename, String source)
		{
			m_filename = source;
			m_source = source;
			CreateTokenStream();
		}

		public List<Token> TokenStream
		{
			get
			{
				return m_tokenStream;
			}
		}

		void CreateTokenStream()
		{
			while (m_currentPosition < m_source.Length)
			{
				int oldPosition = m_currentPosition;
				Token token = GetNextToken();
				if (token != null)
					m_tokenStream.Add(token);
				Debug.Assert(m_currentPosition > oldPosition);
				if (m_currentPosition == oldPosition)
				{
					Console.Error.WriteLine("{0}({1}:{2})Unexpected character", m_filename, currentLine, m_currentPosition - lineStartsAt);
					++m_currentPosition;
				}
			}
			m_tokenStream.Add(CreateToken(TokenType.EndOfStream, m_currentPosition));
		}

		Token GetNextToken()
		{
			char nextChar = m_source[m_currentPosition];

			if (nextChar == '\n' || nextChar == '\r')
				return NewLine();
			if (Char.IsLetter(nextChar))
				return GetWord();
			if (nextChar == ' ')
				return SkipWhiteSpaces();
			if (nextChar == '(')
				return CreateToken(TokenType.OpenBracket, m_currentPosition + 1);
			if (nextChar == ')')
				return CreateToken(TokenType.CloseBracket, m_currentPosition + 1);
			if (nextChar == ':')
				return CreateToken(TokenType.Colon, m_currentPosition + 1);
			if (nextChar == ',')
				return CreateToken(TokenType.Comma, m_currentPosition + 1);
			if (nextChar == '\t')
			{
				if (lineStartsAt == m_currentPosition)
					return GetIndent();
				else
					return SkipWhiteSpaces();
			}
			if (nextChar == '/' && m_source.Length - m_currentPosition >= 2 && m_source[m_currentPosition + 1] == '/')
			{
				return SkipLineComment();
			}
			return null;
		}

		Token SkipLineComment()
		{
			m_currentPosition += 2;
			while (m_currentPosition < m_source.Length)
			{
				if (m_source[m_currentPosition] == '\n' || m_source[m_currentPosition] == '\r')
					break;
				++m_currentPosition;
			}
			return null;
		}

		Token GetWord()
		{
			int endPosition = m_currentPosition;
			while (++endPosition < m_source.Length && Char.IsLetterOrDigit(m_source[endPosition])) ;
			return CreateToken(TokenType.Word, endPosition);
		}

		Token GetIndent()
		{
			int endPosition = m_currentPosition;
			while (++endPosition < m_source.Length && m_source[endPosition] == '\t') ;
			return CreateToken(TokenType.Indent, endPosition);
		}

		Token SkipWhiteSpaces()
		{
			while (++m_currentPosition < m_source.Length)
			{
				char ch = m_source[m_currentPosition];
				if (ch != ' ' || ch != '\t')
					break;
			}
			return null;
		}

		Token NewLine()
		{
			int endPosition = m_currentPosition;
			while (++endPosition < m_source.Length)
			{
				char ch = m_source[endPosition];
				if (ch == '\n')
					currentLine++;
				else if (ch != '\r')
					break;
			}
			Token result = CreateToken(TokenType.NewLine, endPosition);
			lineStartsAt = endPosition;
			return result;
		}

		Token CreateToken(TokenType type, int end)
		{
			Token result = new Token(type, m_source.Substring(m_currentPosition, end - m_currentPosition), currentLine, m_currentPosition - lineStartsAt);
			m_currentPosition = end;
			return result;
		}

		private String m_filename;
		private String m_source;
		private int m_currentPosition = 0;
		private int lineStartsAt = 0;
		private int currentLine = 1;
		private List<Token> m_tokenStream = new List<Token>();
	}
}
